#
# PlanetfallSpells.py
# Contains base class for a PlanetfallSpell.

import CvUtil
from CvPythonExtensions import *

import PlanetfallSpellsInterface
import PlanetfallTargetFactory as TargetFactory
import PlanetfallEntityRenderers as Renderers

gc = CyGlobalContext()

class PlanetfallSpellBase:
    def __init__(self):
        self.m_eSpell = -1

    def getAbstractFunctions(self):
        """
        Arguments:
            None
        Returns:
            A list of strings, each string representing a function name that MUST be overwritten.

        Make sure these function have the __ABSTRACT__ attribute added to them. Then, when PlanetfallSpells
        is initialized, it will automatically check to make sure that the player overrode the function in their
        class.

        When creating a new base spell class that inherits from this or another base spell class, you'll want to
        override this method. First, get a list to return the base's abstract function list (calling baseClass.getAbstractFunctions())
        , where "baseClass" is the name of the base class. Then add any additional ones your base class needs overridden.
        """
        return ["canUnitCast", "onSpellActivate", "startSpellResults"]

    def getSpellNumber(self):
        """
        Arguments:
            None
        Returns:
            This spells SpellTypes number.
        """
        return self.m_eSpell

    def setSpellNumber(self, eSpell):
        """
        Arguments:
            eSpell: This spells SpellTypes number. Used when this spell's object is first initialized.
        Returns:
            None
        """
        self.m_eSpell = eSpell
        

    def canGroupCast(self, pCastingGroup, pCastingPlot, bTestVisible):
        """
        Arguments:
            pCastingGroup:  The group of units who we are checking can or can't cast this spell
            pCastingPlot:   The plot that the group of units will try to cast from. Note that this may
                            be different from their current plot, because this spell might be queued
                            to be done after a move command.
            bTestVisible:   True if we're just testing if the spells button should just be in the bar.
                            False if we're also testing if the button should allow to be selected.
        Returns:
            if bTestVisible is true, then returning "true" allows for the group to have the button in their row of buttons. However,
            the button will only be able to be pushed if this function also returns true when the bTestVisible argument is false.

        Default implementation: If any unit can cast, then then return true.
        """

        
        for pCastingUnit in pCastingGroup.getUnits():
            if self.canUnitCast(pCastingUnit, pCastingPlot, bTestVisible):
                return True
            
        return False
            

    def canUnitCast(self, pCastingUnit, pCastingPlot, bTestVisible):
        """
        This function works similar to canGroupCast, except it deals with units rather than groups.
        This function should be overwritten by a spell's class.

        Arguments:
            pCastingUnit:   The unit who we are checking can or can't cast this spell.
            pCastingPlot    The plot that the unit will try to cast from. Note that this may
                            be different from their current plot, because this spell might be queued
                            to be done after a move command.
            bTestVisible: True if we're just testing if the spell button should be in the list of buttons for this unit.
                            False if we're also testing if the button should allow be allowed to be clicked on.

        Returns:
            If bTestVisible is true, then returning true allows for the unit to have the button in their row of buttons. However,
            the button will only be able to be pushed if this function also returns true when the bTestVisible argument is false.

            Since Civ4 decides if a button is visible based by groups, it should be noted that a button will appear if ANY unit
            of the group returns true here. To change this, overwrite the canGroupCast function of the Spell.
        """
        raise NotImplementedError, "canUnitCast was called, but not implemented"
    canUnitCast.__ABSTRACT__ = True

    def getHelpText(self, pCastingGroup, pPlot):
        """
        Arguments:
            pCastingGroup: The selected group for whom the help text should be displayed.
            pPlot: The plot that the spell would be called on if selected.
        Returns:
            The string that should be shown as the help text, or simply return or pass to use the XML default.

        Override this function to display a help text when a user hovers over a spell's button
        located in the action button list on the bottom of the screen.
        """
        return ""
                            
    def onSpellActivate(self, pCastingGroup, pCastingPlot):
        """
        Arguments:
            pCastingGroup: The CySelectionGroup object that was selected when the spell was activated.
            pCastingPlot: The plot that the group will be on when the spell is cast.

        Returns:
            None

        This function is called when the player clicks (or otherwise using hotkeys selects) the spell to be run. For spells
        that have immediate effects, this should call the pushSpellMission function. Spells that require targetting should
        have this function start the targetting system.
        """
        raise NotImplementedError, "onSpellActivate was called, but has not been overridden."
    onSpellActivate.__ABSTRACT__ = True

    def getUnitHelpText(self, pUnit):
        """
        Arguments:
            pUnit: The CyUnit object representing the unit that we're making the help text for.
        Returns:
            A string that should be shown as the help text.

        Override this function to display a help text after a unit's information in the info pane
        when hovering over a plot. This is typically where you would see something like:
        "Building Road (5 turns)".
        """
        return ""

    def pushSpellMission(self, pTargetData):
        """
        Arguments:
            pTargetData: The 3-tuple that consists of iData1, iData2, and iData3.

        Returns:
            none

        This is used when you wish to push a spell's mission. When you have selected your target and are all set
        to start the spell, you must call this. Because the news of the spell being cast must propegate over the network
        to other computers, any target must be able to fit into three integer variables.
        """
        gc.pushSpellMission(self.getSpellNumber(), pTargetData[0], pTargetData[1], pTargetData[2])

    def startSpellResults(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetData: The 3-tuple consisting of iData1, iData2, and iData3.
        Returns:
            True to continue the spell.
            False or nothing otherwise.

        This is called when the spell effects are actually being done. Kill units, cause havoc, start wars.
        If the spell is a one-time issue, just do what you need and return False (or don't return anything).
        If the spell needs to last a few turns, you should probaby initialize any variables you need
        to keep track of, and then return True here.
        """
        raise NotImplementedError, "startSpellResults was called, but not implemented."
    startSpellResults.__ABSTRACT__ = True

    def continueSpellResults(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetData: The 3-tuple consisting of iData1, iData2, and iData3.
        Returns:
            True to continue the spell.
            False or nothing otherwise.

        This is called when the unit does it's turn casting a spell that takes a few turns. So long as the group is casting the spell and has
        moves left in their turn, this function will be cast. It's advisable that at the end of this function, you end the groups turn by having
        at least one unit in it lose it's movement points.
        """
        raise NotImplementedError, "continueSpellResults was called, but not implemented."
    continueSpellResults.__ABSTRACT__ = True

    def cleanupSpell(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetData: The 3-tuple that consists of iData1, iData2, and iData3.

        Returns:
            None

        This function is called whenever a spell stops being cast. This includes when the spell's duration is over, or if the unit is killed while
        it was still casting, or if the player has decided to stop what the unit is currently doing, thus interrupting the spell.
        This isn't too useful for spell's that just do something simple (one turn), but it is useful for multi-turn spells that
        need to reset any variables they were using, since it's not completely certain that continueSpellResults will be called when you want the
        spell to end.
        """
        pass

class PlanetfallSpellImmediateBase(PlanetfallSpellBase):
    """
    A subclass that can be used to easily allow for "immediate" spells by handling all the nitty gritty of activating the spell.
    """
    def __init__(self):
        PlanetfallSpellBase.__init__(self)

    def onSpellActivate(self, pCastingGroup, pCastingPlot):
        self.pushSpellMission( (-1, -1, -1) )

class PlanetfallSpellPlotSelectBase(PlanetfallSpellBase):
    """
    A subclass that can be used to easily allow for "plot targettinging" spells by handling all the plot drawing and selecting for you.
    This can be used as a base for a spell that targets plot, or for spells that first target plots then call something else to continue
    targetting (for example, this is used as a base for the EntitySelect base, which doesn't target plots, but rather "entities" on the plots.

    By default, it allows all plots to be selected by the spell, so you probably might want to override at least the canSelectPlot
    function to limit this ability. Overriding getTargetPlots will change what plots will be highlighted for targetting when you hover over a
    new plot, and overridding getRangePlots will change what plots will be colored as "range" plots.
    """
    def __init__(self):
        PlanetfallSpellBase.__init__(self)

    def getAbstractFunctions(self):
        pAbstractFunctions = PlanetfallSpellBase.getAbstractFunctions(self)
        pAbstractFunctions.extend( ["onPlotSelect"] )
        return pAbstractFunctions

    def onSpellActivate(self, pCastingGroup, pCastingPlot):
        """
        Arguments:
            pCastingGroup: The CySelectionGroup object that was selected when the spell was activated.
            pCastingPlot: The plot that the group will be on when the spell is cast.

        Returns:
            None

        This function is called when the player clicks (or otherwise using hotkeys selects) the spell to be run.
        This function of the PlotSelect class overrides the default version, and calls the 
        """
        PlanetfallSpellsInterface.showPlotSelectScreen( pCastingGroup, pCastingPlot )
        # TODO: place the following line into the plot screen initialization function.
        self.onPlotHover(pCastingGroup, pCastingPlot, CyInterface().getMouseOverPlot() )

    def onPlotHover(self, pCastingGroup, pCastingPlot, pHoverPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pHoverPlot: The plot that the mouse just hovered over.

        Returns:
            None

        If the user is selecting a plot while targetting a spell, this function is called whenever the user hovers over
        a new plot. It should be used to "highlight" a plot or plots based on what plot is being highlighted.
        """
        self.drawPlots(pCastingGroup, pCastingPlot, pHoverPlot)

    def drawPlots(self, pCastingGroup, pCastingPlot, pHoverPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pHoverPlot: The plot that the mouse just hovered over.

        Returns:
            None

        This goes through and draws all of the coloring of plots. By default, this calls
        drawRangePlots and drawTargetPlots, so you shouldn't really need to change it.
        """
        self.drawRangePlots(pCastingGroup, pCastingPlot, pHoverPlot)
        self.drawTargetPlots(pCastingGroup, pCastingPlot, pHoverPlot)

    def getRangePlots(self, pCastingGroup, pCastingPlot, pHoverPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pHoverPlot: The plot that the mouse just hovered over.

        Returns:
            A list of plots that will be drawn with the "target" plot color. Use this determine what
            plots to highlight as possible targettable plots, similar to how plots are highlighted for
            air missions in base Civ4.

            Return None or or empty list (i.e., "return []") to make no plots colored as "range plots".
        """
        return None

    def getRangePlotColor(self, pCastingGroup, pCastingPlot, pRangePlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pHoverPlot: The plot that needs to be colored as a range plot.

        Returns:
            An NiColorA object that represents the color that will be used. This can be done in two ways:

            1.) On-the-fly color creation:
                return NiColorA(0.2, 0.2, 0.2, 0.6) (Arguments are "Red", "Green", "Blue", and "Alpha")

            2.) Using static colors:
                return gc.getColorInfo( gc.getInfoTypeForString("MY_STATIC_COLOR").getColor() )

        Note that by getting the color for each range plot, it's possible to different colors. For example,
        it's possible to return one color for plots that are in range that are visible, and another for those
        within range that aren't visible.

        This defaults to returning a grey-like targetting scheme.
        """
        return NiColorA(0.2, 0.2, 0.2, 0.6)

    def drawRangePlots(self, pCastingGroup, pCastingPlot, pHoverPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pHoverPlot: The plot that is currently being "hovered" over with the mouse.

        Returns:
            None

        This just calls getRangePlots to get all the range plots, and then goes through drawing them all. In most cases,
        it shouldn't need to be overridden.
        """
        pRangePlots = self.getRangePlots(pCastingGroup, pCastingPlot, pHoverPlot)
        if pRangePlots is None:
            return

        for pPlot in pRangePlots:
            CyEngine().addColoredPlot(pPlot.getX(), pPlot.getY(), self.getRangePlotColor(pCastingGroup, pCastingPlot, pPlot), PlanetfallSpellsInterface.PLANETFALL_SPELL_PLOT_LAYER )

    def getTargetPlots(self, pCastingGroup, pCastingPlot, pPotentialTargetPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell.
            pPotentialTargetPlot: The plot that the user has highlighted.

        Returns:
            A list of plots that will be drawn as "Target Plots"

        By default, this just returns the hovered plot. However, this can be
        overridden to allow for, say, a 3x3 block of plots to be highlighted.
        Or, to have no plots highlited at all by returning None or [].
        """
        return [pPotentialTargetPlot]

    def canTargetPlot(self, pCastingGroup, pCastingPlot, pPotentialTargetPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pPotentialTargetPlot: The plot that the user clicked.

        Returns:
            True or False, depending on if the plot can be targetted.

        Used for when a user is selecting a plot. If the function returns true, then the plot is selected, and
        onPlotSelect will be called. If this returns false, then the user continues trying to select a plot, and their attempt
        to select this one fails.

        This can also be quite useful for making range plots be drawn differently by calling this function to determine what color to draw it.
        """
        return True

    def getTargetPlots(self, pCastingGroup, pCastingPlot, pPotentialTargetPlot):
        """
        Arguments:
            pCastingGroup: The Selection Group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell.
            pPotentialTargetPlot: The plot that the user has highlighted.

        Returns:
            A list of plots that will be drawn as "Target Plots"

        By default, this just returns the hovered plot. However, this can be
        overridden to allow for, say, a 3x3 block of plots to be highlighted.
        Or, to have no plots highlited at all by returning None or [].
        """
        return [pPotentialTargetPlot]

    def drawTargetPlots(self, pCastingGroup, pCastingPlot, pHoverPlot):
        pTargetPlots = self.getTargetPlots( pCastingGroup, pCastingPlot, pHoverPlot )

        if pTargetPlots is None:
            return

        for pPlot in pTargetPlots:
            if self.canTargetPlot(pCastingGroup, pCastingPlot, pPlot):
                CyEngine().addColoredPlot(pPlot.getX(),pPlot.getY(), NiColorA(0.0, 1.0, 0.0, 1.0), PlanetfallSpellsInterface.PLANETFALL_SPELL_PLOT_LAYER )

    def onPlotSelect(self, pCastingGroup, pCastingPlot, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pTargetPlot: The plot clicked on.

        Returns:
            None

        Runs the script of what happens when a plot is selected. This must be overridden
        by the class inheriting this base.
        """
        raise NotImplementedError, "onPlotSelect was called, but not implemented"
    onPlotSelect.__ABSTRACT__ = True

        


class PlanetfallSpellPlotTargetBase(PlanetfallSpellPlotSelectBase):
    """
    A subclass that can be used to easily allow for "plot targetting" spells by handling all the plot drawing and selecting for you.

    By default, it allows all plots to be selected by the spell, so you probably might want to override at least the canSelectPlot
    function to limit this ability. Overriding getTargetPlots will change what plots will be highlighted for targetting when you hover over a
    new plot, and overridding getRangePlots will change what plots will be colored as "range" plots.
    """
    def __init__(self):
        PlanetfallSpellPlotSelectBase.__init__(self)

    def getAbstractFunctions(self):
        pAbstractFunctions = PlanetfallSpellPlotSelectBase.getAbstractFunctions(self)
        pAbstractFunctions.extend( ["startSpellResultsOnPlot"] )
        return pAbstractFunctions

    def onPlotSelect(self, pCastingGroup, pCastingPlot, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pTargetPlot: The plot clicked on.

        Returns:
            None

        Runs the script of what happens when a plot is clicked. Note that by default, this function calls
        canTargetPlot to make sure the target clicked is legit.

        By default, the PlanetfallSpellPlotSelect class has this function pushing the spell mission, along with
        stopping the plot select screen and clearing all plot coloring.
        """
        if self.canTargetPlot(pCastingGroup, pCastingPlot, pTargetPlot):
            self.pushSpellMission( TargetFactory.getPlotTargetFromPlot(pTargetPlot).getData() )
            PlanetfallSpellsInterface.hidePlotSelectScreen()
            CyEngine().clearColoredPlots( PlanetfallSpellsInterface.PLANETFALL_SPELL_PLOT_LAYER )

    def startSpellResults(self, pCastingGroup, pTargetData):
        self.startSpellResultsOnPlot(pCastingGroup, TargetFactory.getPlotTargetFromData(pTargetData).getPlot())

    def startSpellResultsOnPlot(self, pCastingGroup, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetPlot: The plot targetted by the spell.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of startSpellResults when using the PlotTargetBase,
        as it already converts the pTargetData into a CyPlot object for you to use.
        """
        raise NotImplementedError, "startSpellResultsOnPlot was called, but not implemented."
    startSpellResultsOnPlot.__ABSTRACT__ = True

    def continueSpellResults(self, pCastingGroup, pTargetData):
        self.continueSpellResultsOnPlot(pCastingGroup, TargetFactory.getPlotTargetFromData(pTargetData).getPlot() )

    def continueSpellResultsOnPlot(self, pCastingGroup, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetPlot: The plot targetted by the spell.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of continueSpellResults when using the PlotTargetBase,
        as it already converts the pTargetData into a CyPlot object for you to use.
        """
        raise NotImplementedError, "continueSpellResultsOnPlot was called, but not implemented."

    def cleanupSpell(self, pCastingGroup, pTargetData):
        self.cleanupSpellOnPlot(pCastingGroup, TargetFactory.getPlotTargetFromData(pTargetData).getPlot() )

    def cleanupSpellOnPlot(self, pCastingGroup, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetPlot: The plot targetted by the spell.

        Returns:
            None

        This function should be used instead of cleanupSpell when using the PlotTargetBase,
        as it already converts the pTargetData into a CyPlot object for you to use.
        """
        pass
    
        

class PlanetfallSpellEntityTargetBase(PlanetfallSpellPlotSelectBase):
    """
    A subclass that can be used to easily allow for "entity targetting" spells
    by handling all the plot drawing and unit-targetting drawing for you. An entity is something
    that can be represented on a "table" of items. Examples are units, buildings, improvements, religions,
    etc. Note that there are some pre-built classes that handle the main ones already (units, buildings, etc.)
    This class can be used as a base for creating your own.

    By default, it allows all plots to be selected by the spell, so you probably might
    want to override at least the canSelectPlot function to limit this ability.

    Also, you will have to override the "getColumnNames", "renderEntity",
    and "getPlotEntityTargets" functions.
    """
    def __init__(self):
        PlanetfallSpellPlotSelectBase.__init__(self)

    def getAbstractFunctions(self):
        pAbstractFunctions = PlanetfallSpellPlotSelectBase.getAbstractFunctions(self)
        pAbstractFunctions.extend( ["renderEntity", "getPlotEntityTargets", "onEntitySelect", "startSpellResults"] )
        return pAbstractFunctions

    def renderEntity(self, pEntityRow, pEntityTarget):
        """
        Arguments:
            pEntityRow: The EntityRow class that is used to create the
                        visual appearance of the entity's row on the screen.
            pUnitTarget: The UnitTarget that is being "rendered"
        Returns:
            None

        This function should call functions in the pEntityRow item to add
        various widgets to the screen for the entity pEntityTarget.
        """
        
        raise NotImplementedError, "This function must be implemented!"
    renderEntity.__ABSTRACT__ = True

    def getPlotEntityTargets(self, pCastingGroup, pCastingPlot, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selectoin group will cast the spell from.
            pTargetPlot: The plot clicked on.
        Returns:
            A list of items that should be shown on the entity select screen.
        """
        raise NotImplementedError, "This function must be implemented!"
    getPlotEntityTargets.__ABSTRACT__ = True

    def onPlotSelect(self, pCastingGroup, pCastingPlot, pTargetPlot):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selectoin group will cast the spell from.
            pTargetPlot: The plot clicked on.

        Returns:
            None

        Tells the spells engine to load up the entity-chooser dialog.
        """
        if self.canTargetPlot(pCastingGroup, pCastingPlot, pTargetPlot):
            PlanetfallSpellsInterface.hidePlotSelectScreen()
            CyEngine().clearColoredPlots( PlanetfallSpellsInterface.PLANETFALL_SPELL_PLOT_LAYER )
            pTargets = self.getPlotEntityTargets(pCastingGroup, pCastingPlot, pTargetPlot)
            if len(pTargets) == 0:
                PlanetfallSpellsInterface.cancelSpell(pCastingGroup, pCastingPlot)
                return
            PlanetfallSpellsInterface.showEntitySelectScreen( pCastingGroup, pCastingPlot, pTargets, self.renderEntity )

    def onEntitySelect(self, pCastingGroup, pCastingPlot, pEntity):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selection group will cast the spell from.
            pEntity: The entity selected.
        Returns:
            None
        """
        raise NotImplementedError, "This function must be implemented!"
    onEntitySelect.__ABSTRACT__ = True

    def startSpellResults(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetData: The 3-tuple consisting of iData1, iData2, and iData3.
        Returns:
            True to continue the spell.
            False or nothing otherwise.

        This is called when the spell effects are actually being done. Kill units, cause havoc, start wars.
        If the spell is a one-time issue, just do what you need and return False (or don't return anything).
        If the spell needs to last a few turns, you should probaby initialize any variables you need
        to keep track of, and then return True here.
        """
        raise NotImplementedError, "This function must be implemented!"
    startSpellResults.__ABSTRACT__ = True

    def continueSpellResults(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetData: The 3-tuple consisting of iData1, iData2, and iData3.
        Returns:
            True to continue the spell.
            False or nothing otherwise.

        This is called when the unit does it's turn casting a spell that takes a few turns. So long as the group is casting the spell and has
        moves left in their turn, this function will be cast. It's advisable that at the end of this function, you end the groups turn by having
        at least one unit in it lose it's movement points.
        """
        raise NotImplementedError, "This function must be implemented!"


    def cleanupSpell(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetData: The 3-tuple that consists of iData1, iData2, and iData3.

        Returns:
            None

        This function is called whenever a spell stops being cast. This includes when the spell's duration is over, or if the unit is killed while
        it was still casting, or if the player has decided to stop what the unit is currently doing, thus interrupting the spell.
        This isn't too useful for spell's that just do something simple (one turn), but it is useful for multi-turn spells that
        need to reset any variables they were using, since it's not completely certain that continueSpellResults will be called when you want the
        spell to end.
        """
        pass

class PlanetfallSpellUnitTargetBase(PlanetfallSpellEntityTargetBase):     
    """
    A subclass that can be used to easily allow for "unit targetting" screens.
    Use this class if you want to have a spell that, after choosing a plot,
    will show a list of units to choose from.

    This class will handle the creation of the unit targeting screen and
    all events tied to it.

    By default, it allows only plots with units to be targetted by the spell,
    so you probably might want to override at least the canSelectPlot function
    (say, to limit it to plots with just enemy or just friendly units).
    Also, overriding the canSelectUnit function allows you to limit what units
    will be displayed in the screen. By default, all VISIBLE units to the casting
    player will be displayed.
    """
    def __init__(self):
        PlanetfallSpellEntityTargetBase.__init__(self)

    def getAbstractFunctions(self):
        pAbstractFunctions = PlanetfallSpellEntityTargetBase.getAbstractFunctions(self)
        pAbstractFunctions.extend( ["startSpellResultsOnUnit"] )
        return pAbstractFunctions

    def renderEntity(self, pEntityRow, pUnitTarget):
        """
        Arguments:
            pEntityRow: The EntityRow class that is used to create the
                        visual appearance of the entity's row on the screen.
            pUnitTarget: The UnitTarget that is being "rendered"
        Returns:
            None

        This function should call functions in the pEntityRow item to add
        various widgets to the screen for the entity pEntityTarget.
        """
        Renderers.renderUnit(pEntityRow, pUnitTarget.getUnit())

    def getPlotEntityTargets(self, pCastingGroup, pCastingPlot, pTargetPlot):
        pTargets = []
        for iUnit in range(0, pTargetPlot.getNumUnits()):
            pUnit = pTargetPlot.getUnit(iUnit)
            if not pUnit.isInvisible(pCastingGroup.getTeam(), False):
                pTargets.append( TargetFactory.getUnitTargetFromUnit(pUnit) )

        return pTargets

    def onEntitySelect(self, pCastingGroup, pCastingPlot, pTargetUnit):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selectoin group will cast the spell from.
            pTargetUnit: The TargetUnit object that represents the targetted unit.
        Returns:
            None
        """
        self.pushSpellMission( pTargetUnit.getData() )
        PlanetfallSpellsInterface.hideEntitySelectScreen()

    def startSpellResults(self, pCastingGroup, pTargetData):
        self.startSpellResultsOnUnit(pCastingGroup, TargetFactory.getUnitTargetFromData(pTargetData).getUnit() )

    def startSpellResultsOnUnit(self, pCastingGroup, pTargetUnit):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetUnit: The unit targetted by the spell.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of startSpellResults when using the UnitTargetBase,
        as it already converts the pTargetData into a CyUnit object for you to use.
        """
        raise NotImplementedError, "startSpellResultsOnUnit was called, but not implemented."
    startSpellResultsOnUnit.__ABSTRACT__ = True

    def continueSpellResults(self, pCastingGroup, pTargetData):
        self.continueSpellResultsOnUnit(pCastingGroup, TargetFactory.getUnitTargetFromData(pTargetData).getUnit() )

    def continueSpellResultsOnUnit(self, pCastingGroup, pTargetUnit):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetUnit: The unit targetted by the spell.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of continueSpellResults when using the UnitTargetBase,
        as it already converts the pTargetData into a CyUnit object for you to use.
        """
        raise NotImplementedError, "continueSpellResultsOnUnit was called, but not implemented."

    def cleanupSpell(self, pCastingGroup, pTargetData):
        self.cleanupSpellOnUnit(pCastingGroup, TargetFactory.getUnitTargetFromData(pTargetData).getUnit() )

    def cleanupSpellOnUnit(self, pCastingGroup, pTargetData):
        """
        Arguments:
            pCastingGroup: The group casting the spell.
            pTargetUnit: The unit targetted by the spell.

        Returns:
            None

        This function should be used instead of cleanupSpell when using the UnitTargetBase,
        as it already converts the pTargetData into a CyUnit object for you to use.
        """
        pass

class PlanetfallSpellBuildingTargetBase(PlanetfallSpellEntityTargetBase):     
    """
    A subclass that can be used to easily allow for "building targetting" screens.
    Use this class if you want to have a spell that, after choosing a plot,
    will show a list of buildings from the city of the selected plot.

    This class will handle the creation of the unit targeting screen and
    all events tied to it.

    By default, it allows only plots with cities to be targetted by spells,
    so you probably might want to override at least the canSelectPlot function
    (say, to limit it to plots with just enemy or just friendly cities).
    Also, overriding the canSelectBuilding function allows you to limit what buildings
    will be displayed in the screen. By default, all buildings will be displayed.
    """
    def __init__(self):
        PlanetfallSpellEntityTargetBase.__init__(self)

    def getAbstractFunctions(self):
        pAbstractFunctions = PlanetfallSpellEntityTargetBase.getAbstractFunctions(self)
        pAbstractFunctions.extend( ["startSpellResultsOnBuilding"] )
        return pAbstractFunctions

    def renderEntity(self, pEntityRow, pBuildingTarget):
        """
        Arguments:
            pEntityRow: The EntityRow class that is used to create the
                        visual appearance of the entity's row on the screen.
            pBuildingTarget: The BuildingTarget that is being "rendered" to the row.
        Returns:
            None

        This function should call functions in the pEntityRow item to add
        various widgets to the screen for the entity pEntityTarget.
        """
        Renderers.renderBuilding(pEntityRow, pBuildingTarget.getCity(), pBuildingTarget.getBuilding())

    def getPlotEntityTargets(self, pCastingGroup, pCastingPlot, pTargetPlot):
        pTargets = []

        pCity = pTargetPlot.getPlotCity()
        if (pCity is not None):
            for iBuilding in range(gc.getNumBuildingInfos()):
                if (pCity.hasBuilding(iBuilding)):
                    pTargets.append( TargetFactory.getBuildingTargetFromCityBuilding(pCity, iBuilding) )

        return pTargets

    def onEntitySelect(self, pCastingGroup, pCastingPlot, pTargetBuilding):
        """
        Arguments:
            pCastingGroup: The selection group that is casting the spell.
            pCastingPlot: The plot that the selectoin group will cast the spell from.
            pTargetBuilding: The BuildingTarget target.
        Returns:
            None
        """
        self.pushSpellMission( pTargetBuilding.getData() )
        PlanetfallSpellsInterface.hideEntitySelectScreen()

    def startSpellResults(self, pCastingGroup, pTargetData):
        pBuildingTarget = TargetFactory.getBuildingTargetFromData(pTargetData)
        pCity = pBuildingTarget.getCity()
        iBuilding = pBuildingTarget.getBuilding()
        self.startSpellResultsOnBuilding(pCastingGroup, pCity, iBuilding )

    def startSpellResultsOnBuilding(self, pCastingGroup, pTargetCity, iTargetBuilding):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetCity: The city that has the building targetted.
            iBuilding: The type of building targetted.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of startSpellResults when using the PlanetfallSpellBuildingTargetBase
        as it already converts the pTargetData into CyCity and iBuilding variables for you to use.
        """
        raise NotImplementedError, "startSpellResultsOnBuilding was called, but not implemented."
    startSpellResultsOnBuilding.__ABSTRACT__ = True

    def continueSpellResults(self, pCastingGroup, pTargetData):
        pBuildingTarget = TargetFactory.getBuildingTargetFromData(pTargetData)
        pCity = pBuildingTarget.getCity()
        iBuilding = pBuildingTarget.getBuilding()
        self.continueSpellResultsOnBuilding(pCastingGroup, pCity, iBuilding )

    def continueSpellResultsOnBuilding(self, pCastingGroup, pTargetCity, iTargetBuilding):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetCity: The city that has the building targetted.
            iBuilding: The type of building targetted.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of continueSpellResults when using the PlanetfallSpellBuildingTargetBase
        as it already converts the pTargetData into CyCity and iBuilding variables for you to use.
        """
        raise NotImplementedError, "continueSpellResultsOnBuilding was called, but not implemented."

    def cleanupSpell(self, pCastingGroup, pTargetData):
        pBuildingTarget = TargetFactory.getBuildingTargetFromData(pTargetData)
        pCity = pBuildingTarget.getCity()
        iBuilding = pBuildingTarget.getBuilding()
        self.cleanupSpellOnBuilding(pCastingGroup, pCity, iBuilding )

    def cleanupSpellOnBuilding(self, pCastingGroup, pTargetCity, iBuilding ):
        """
        Arguments:
            pCastingGroup: The group casting the spell
            pTargetCity: The city that has the building targetted.
            iBuilding: The type of building targetted.
        Returns:
            True to continue the spell
            False or nothing otherwise

        This function should be used instead of cleanupSpell when using the PlanetfallSpellBuildingTargetBase
        as it already converts the pTargetData into CyCity and iBuilding variables for you to use.
        """
        pass
